#!/usr/bin/env python
# coding=utf-8
import os
import sys
import socket
import struct
import hashlib
import time
import re
import subprocess
import traceback
import uuid

reload(sys)
sys.setdefaultencoding("utf-8")

BUFFER_SIZE = 512
# download file store path to current user home directory
DOWNLOAD_PATH = r'/opt'
# after download finish save filename,software package name
abc = str(uuid.uuid1())
DOWNLOAD_FILE_NAME = os.path.join(DOWNLOAD_PATH, abc+'-00.run')
# tmp file user to save download file data
DOWNLOAD_FILE_TMP = os.path.join(DOWNLOAD_PATH, abc+'download.tmp')
# source file md5 value
SRC_FILE_MD5 = ""
# log file
LOG_FILE = '/opt/ftp_client.log'
# ftp lock
FTP_LOCK = '/root/ftp.lock' + abc
FTP_SUCCESS_LOCK = '/root/ftp.success' +abc
# HEAD STRUCT
HEAD_STRUCT = '128sIq32s'
# FTP_SERVER_IP
FTP_SERVER_IP = None
# FTP_SERVER_PORT
FTP_SERVER_PORT = 63128


def record_log(level, msg):
    # print('[' + level + ']   ' + msg)
    log_time = time.strftime('[%Y-%m-%d-%H-%M-%S]', time.localtime(time.time()))
    logfile = os.path.normpath(LOG_FILE)
    fp = open(logfile, 'a+')
    if not fp:
        return
    try:
        msg = '[' + level + ']   ' + log_time + '   ' + msg
        fp.write(msg + "\n")
    except Exception, e:
        record_log('ERROR', str(traceback.format_exc()))
    finally:
        fp.close()
    pass


def check_net(ip):
    try:
        cmd = 'ping -c 1 ' + ip
        cmd_result = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
        read_cmd_result = cmd_result.stdout.read()
        # use regex
        regex = re.compile('100% packet loss')
        if len(regex.findall(read_cmd_result)) == 0:
            return True
        else:
            return False
    except:
        record_log('ERROR', str(traceback.format_exc()))
        sys.exit(-1)


def get_md5(input_file):
    rb = open(input_file, 'rb')
    md5 = hashlib.md5()
    md5.update(rb.read())
    return md5.hexdigest()


def get_run_file_name():
    # 遍历DOWNLOAD_PATH下所有run文件
    run_file = None
    try:
        files = os.listdir(DOWNLOAD_PATH)
        for fi in files:
            tmp_file = os.path.join(DOWNLOAD_PATH, fi)
            if os.path.isfile(tmp_file) and fi.endswith('.run'):
                run_file = tmp_file
                break
    except:
        record_log('ERROR', str(traceback.format_exc()))

    return run_file


def ftp_client(host, port):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # s.settimeout(1)
    s.connect((host, port))
    # s.setblocking(0)
    file_head_size = struct.calcsize(HEAD_STRUCT)
    fhead = s.recv(file_head_size)
    global SRC_FILE_MD5
    #global DOWNLOAD_FILE_NAME
    src_file_name_raw, src_file_len, src_file_size, SRC_FILE_MD5 = struct.unpack(HEAD_STRUCT, fhead)
    src_file_name = str(src_file_name_raw)[:src_file_len]
    record_log('INFO', 'src_file_name: %s, src_file_len: %s' % (src_file_name, src_file_len))
    #DOWNLOAD_FILE_NAME = os.path.normpath(os.path.join(DOWNLOAD_PATH, str(src_file_name).replace("\r", "").strip()))
    if not os.path.exists(DOWNLOAD_FILE_TMP):
        os.system('touch ' + DOWNLOAD_FILE_TMP)

    file_name = os.path.basename(DOWNLOAD_FILE_TMP)
    file_len = len(file_name)
    file_size = os.path.getsize(DOWNLOAD_FILE_TMP)
    file_md5 = get_md5(DOWNLOAD_FILE_TMP)
    fhead_old = struct.pack(HEAD_STRUCT, file_name, file_len, file_size, file_md5)
    s.send(fhead_old)

    with open(DOWNLOAD_FILE_TMP, 'a') as f:
        f.seek(file_size, 0)
        old_percent = 0
        cur_file_size = 0
        while True:
            try:
                data = s.recv(BUFFER_SIZE)
                if not len(data):
                    break
                f.write(data)
                cur_file_size += len(data)
                new_percent = cur_file_size * 100 / src_file_size
                if new_percent > old_percent:
                    old_percent = new_percent
                    record_log('INFO', 'recv percent.................%s%%, [%s: %s] ' % (new_percent, cur_file_size, src_file_size))
                if cur_file_size == src_file_size:
                    record_log('INFO', 'recv size is %s' % cur_file_size)
                    break

            except Exception, e:
                record_log('ERROR', str(traceback.format_exc()))
                break
    f.close()  # close file desc
    s.close()  # close connect


if __name__ == '__main__':
    if len(sys.argv) == 2:
        #if os.path.exists(LOG_FILE):
        #    os.remove(LOG_FILE)
        record_log('INFO', 'download begin, then lock it!')
        if not os.path.exists(FTP_LOCK):
            os.mknod(FTP_LOCK)
        FTP_SERVER_IP = sys.argv[1]
        #run_file = get_run_file_name()
        #if run_file:
        #    os.mknod(FTP_SUCCESS_LOCK)
        #    os.unlink(FTP_LOCK)
        #    record_log('INFO', run_file + ' has  exist ')
        #    sys.exit(0)

        if not check_net(FTP_SERVER_IP):
            record_log('ERROR', 'ftp server ' + str(FTP_SERVER_IP) + ' unreachable')
            os.unlink(FTP_LOCK)
            sys.exit(-1)

        # define a most download time
        try:
            ftp_client(FTP_SERVER_IP, FTP_SERVER_PORT)
            dst_file_md5 = get_md5(DOWNLOAD_FILE_TMP)
            record_log('INFO', 'srcMd5: ' + SRC_FILE_MD5 + ', dstMd5: ' + dst_file_md5)
            if dst_file_md5 == SRC_FILE_MD5:
                os.rename(DOWNLOAD_FILE_TMP, DOWNLOAD_FILE_NAME)
                record_log('INFO', DOWNLOAD_FILE_TMP + "   ---       " + DOWNLOAD_FILE_NAME)
                os.mknod(FTP_SUCCESS_LOCK)
                record_log('INFO', "download successfully and save to %s" % DOWNLOAD_FILE_NAME)
            else:
                # os.remove(DOWNLOAD_FILE_TMP)
                record_log('ERROR', 'download failed[' + DOWNLOAD_FILE_NAME + '] from ' + str(FTP_SERVER_IP) + ' because of md5 error!')
        except Exception, e:
            record_log('ERROR', str(traceback.format_exc()))

        if os.path.exists(DOWNLOAD_FILE_NAME):
            record_log('INFO', 'download successfully, then unlock it!')
        elif os.path.exists(DOWNLOAD_FILE_TMP):
            record_log('INFO', 'disconnected ' + str(FTP_SERVER_IP) + ' ftp server')
        else:
            record_log('INFO', str(FTP_SERVER_IP) + ' no open ftp server')

        os.unlink(FTP_LOCK)
        sys.exit(0)
    else:
        print 'usage: python %s server_ip ' % sys.argv[0]
        sys.exit(-1)